前端大文件上传 您所在的位置:网站首页 js 文件分片 前端大文件上传

前端大文件上传

2023-09-29 02:18| 来源: 网络整理| 查看: 265

问题分析

文件过大一次性上传如果中断,只能重来,体验极差。

如果已经上传过,再次上传,浪费存储空间,浪费用户时间。

如此,我们需要做一个支持断点续传,同时支持秒传的方法。

实现思路

后端使用md5标记每一个上传文件,每次上传文件时,前端计算文件md5,然后计算文件分片数,请求后端,如果存在,则秒传。

如果不存则返回当前md5文件上传所有分片的上传地址列表(url带一次性上传权限,防止有人利用恶意利用上传文件接口占用空间),以及如果文件上传过,没上传完,则返回应该从第几个分片继续上传。

然后开启分片上传的并发控制,上传完毕,进行文件合并。

代码参考 选择文件 let chunkSize=6*1024*1024//大于6MB的分片上传 addFile(event) { let file = event.target.files[0] let size = file.size; this.loading = true if(file.size>chunkSize){ //如果大于基本分片单位大小,分片上传 this.uploadFile(file,event,(str)=>{ this.uploadMessage=str }) }else{ //小于基本分片单位大小,直接上传即可,也无需合并 let param = new FormData(); param.append('file', file); utilsApi.postMinio(param).then(res => { //上传成功 this.loading = false }).catch(e => { this.loading=false }) } } MD5计算

这里使用的依赖:"spark-md5": "^3.0.1"

/** * 获取文件MD5 * @param file * @returns {Promise} */ import SparkMD5 from 'spark-md5' getFileMd5(file){ let fileReader = new FileReader() fileReader.readAsBinaryString(file) let spark = new SparkMD5() return new Promise((resolve) => { fileReader.onload = (e) => { spark.appendBinary(e.target.result) resolve(spark.end()) } }) } 分片和判断上传类型 uploadFile(file,callback){ //文件大小 const fileSize = file.size //计算当前选择文件需要的分片数量 let chunkCount = Math.ceil(fileSize / chunkSize)-1 chunkCount=chunkCount>0?chunkCount:1 // console.log("文件大小:",(file.size / 1024 / 1024) + "Mb","分片数:",chunkCount) let that=this //获取文件md5 this.getFileMd5(file).then(fileMd5=>{ // console.log("文件md5:",fileMd5) // console.log("向后端请求本次分片上传初始化") //向后端请求本次分片上传初始化 const initUploadParams = JSON.stringify({chunkCount: chunkCount,fileMd5: fileMd5}) utilsApi.sliceUpload(initUploadParams).then(res=>{ this.count=0 if (res.data == null) { // console.log("当前文件上传情况:所有分片已在之前上传完成,仅需合并") this.composeFile(fileMd5,file.name,event) return; } if (res.msg == "上传完成") { console.log("file url:"+res.data) // console.log("当前文件上传情况:秒传") this.loading=false return } const chunkUploadUrls = res.data //正常开始并发上传 this.multiRequest(chunkUploadUrls,5,file,fileMd5).then(val=>{ // 上传完毕合并分片 this.composeFile(fileMd5,file.name,event) }).catch(err=>{ that.loading=false }) }).catch(val=>{ that.loading=false }) }).catch(err=>{ that.loading=false }) }, 并发请求控制 //并发控制 multiRequest(chunkUploadUrls = [], maxNum,file,fileMd5) { // 请求总数量 const len = chunkUploadUrls.length; // 根据请求数量创建一个数组来保存请求的结果 const result = new Array(len).fill(false); // 当前完成的数量 let count = 0; // console.log("开始promise") let that=this let finish=0//完成数量 return new Promise((resolve, reject) => { // 请求maxNum个 while (count < maxNum) { next(); } //递归和promise控制并发请求数量,启动maxNum个请求,每完成一个启动一个新的 //直到所有的请求完毕,然后请求合并接口 function next(){ try{ let ind = count++; // 处理边界条件 if (ind >= len ) { // console.log("完成了") // 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回 !result.includes(false) && resolve(result); return; } // console.log(`开始 ${ind}`, new Date().toLocaleString()); let item=chunkUploadUrls[ind] //分片开始位置 let start = (item.partNumber - 1) * chunkSize //分片结束位置 let end = 0 if(ind==chunkUploadUrls.length-1){ end=file.size }else{ end =start + chunkSize } //取文件指定范围内的byte,从而得到分片数据 let _chunkFile = file.slice(start, end) // console.log("end-start:",end-start) let last=0 let param = new FormData(); //创建form对象 let url=item.url param.append('key',fileMd5+"-"+(ind+1)+".chunk"); param.append('file', _chunkFile); utilsApi.postMinio({file:param,url:url }).then(res=>{ finish++ // console.log("第" + that.count+ "个分片上传完成") // that.uploadMessage="上传"+ (that.count/ chunkUploadUrls.length * 100 | 0) + '%' result[ind] = true; // console.log(`完成 ${ind}`, new Date().toLocaleString()); // 请求没有全部完成, 就递归 if (ind < len) { next(); } }).catch(err=>{ reject("上传中断") }) }catch(err){ //异常中断 reject(err) } } });


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有